package com.abucloud.logbackdemo.log;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.Objects;

/**
 * @Description:
 * @Author party-abu
 * @Date 2022/5/22 14:59
 */
@Slf4j
@Component
@Aspect
public class LogAspect {

    @Pointcut("execution(* com.abucloud.logbackdemo.controller.*.*(..)) ")
    public void logPoint() {
    }

    /**
     * handler前置处理
     *
     * @param joinPoint
     */
    @Before("logPoint()")
    public void beforeHandlerLog(JoinPoint joinPoint) {

        HttpServletRequest request = this.getHttpServletRequest();
        if (request == null) {
            return;
        }

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        log.info("请求地址：{}，访问接口：{}",
                request.getRequestURL(),
                signature.getDeclaringTypeName() + ":" + signature.getName());

        // 获取并记录参数信息, 取自定义的参数, 不包括Servlet原生api
        this.printRequestParams(joinPoint, signature.getMethod());

        MDC.put("beginRequestTime", String.valueOf(System.currentTimeMillis()));
    }

    /**
     * handler后置处理
     *
     * @param returnValue
     */
    @AfterReturning(value = "logPoint()", returning = "returnValue")
    public void afterHandlerLog(Object returnValue) {

        Map<String, String> mdcMap = MDC.getCopyOfContextMap();
        String beginRequestTime = mdcMap.getOrDefault("beginRequestTime", "");
        long requestTime = 0L;
        if (StringUtils.hasText(beginRequestTime)) {
            requestTime = System.currentTimeMillis() - Long.parseLong(beginRequestTime);
        }

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("spendTime", requestTime + "毫秒");
        jsonObject.put("response", returnValue);
        log.info("响应结果：{}", JSON.parseObject(jsonObject.toString()));

    }

    /**
     * 打印请求参数
     * 思路：排除原生Servlet的API，通过反射拿到handler中参数名列表和参数值列表进行组合
     *
     * @param joinPoint
     * @param method
     */
    private void printRequestParams(JoinPoint joinPoint, Method method) {

        Parameter[] parameters = method.getParameters();
        Object[] args = joinPoint.getArgs();

        JSONObject jsonObject = new JSONObject();

        for (int i = 0; i < parameters.length; i++) {

            Object currentArgs = args[i];
            String currentParameterName = parameters[i].getName();

            // 排除Servlet原生API和MultipartFile
            if (Objects.isNull(currentArgs)
                    || currentArgs instanceof MultipartFile
                    || currentArgs instanceof MultipartFile[]
                    || currentArgs instanceof HttpServletRequest
                    || currentArgs instanceof HttpServletResponse) {
                continue;
            }

            jsonObject.put(currentParameterName, currentArgs);
        }

        log.info("请求参数：{}", JSON.parseObject(jsonObject.toJSONString()));
    }

    /**
     * 获取 HttpServletRequest
     *
     * @return
     */
    private HttpServletRequest getHttpServletRequest() {
        ServletRequestAttributes requestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }
        return requestAttributes.getRequest();
    }
}